#include "defines.h"
#include "kozos.h"
#include "lib.h"
#include "kernel.h"

typedef struct _kzmem_block {
    struct _kzmem_block *next;
    int size;
    int index;
} kzmem_block;

typedef struct _kzmem_pool {
    int size; /* ヘッダ含めないサイズ。偶数にすること(ワードアクセスのアラインメント) */
    int num;
    kzmem_block *free;
} kzmem_pool;

static kzmem_pool pool[] = {
    { 32 , 32, NULL },
    { 128, 32, NULL },
    { 512, 32, NULL },
    { 2048, 32, NULL },
};

void resource_memory(kz_resourceinfo_t *infop)
{
    kzmem_pool *pp;
    for (pp = pool; pp < pool + MEMORY_AREA_NUM; pp++) {
        int c = 0;
        kzmem_block *q;

        for (q = pp->free; q; q = q->next)
            c++;

        infop->type = RESOURCE_MEMORY;
        infop->size = pp->size;
        infop->num = pp->num;
        infop->used = pp->num - c;
        infop++;
    }

}

static void kzmem_init_pool(kzmem_pool *p, int index)
{
    int i;
    kzmem_block *mp;
    kzmem_block **mpp;
    extern char freearea, efreearea;
    static char *area = &freearea;

    mpp = &p->free;
    for (i = 0; i < p->num; i++) {
        ASSERT(!(p->size & 1), "kzmem_init_pool: odd size");
        ASSERT(area + p->size + sizeof(kzmem_block) <= &efreearea,
               "kzmem_init_pool: memory overflow");
        mp = (kzmem_block *)area;
        *mpp = mp;
        mp->next = NULL;
        mp->size = p->size;
        mp->index = index;
        mpp = &mp->next;
        area += p->size + sizeof(kzmem_block);
    }
}

void kzmem_init(void)
{
    int i;

    ASSERT(MEMORY_AREA_NUM == (sizeof(pool) / sizeof(*pool)), "Bad MEMORY_AREA_NUM");

    for (i = 0; i < MEMORY_AREA_NUM; i++) {
        kzmem_init_pool(&pool[i], i);
    }
}

void *kzmem_alloc(int size)
{
    kzmem_block *mp;
    kzmem_pool *p;

    for (p = pool; p < pool + MEMORY_AREA_NUM; p++) {
        if (size <= p->size && p->free) {
            mp = p->free;
            p->free = mp->next;
            mp->next = NULL;
            return mp + 1;
        }
    }

    ASSERT(0, "kzmem_alloc failed");
    return NULL;
}

void kzmem_free(void *mem)
{
    kzmem_block *mp;
    kzmem_pool *p;

    mp = (kzmem_block *)mem - 1;
    ASSERT(0 <= mp->index && mp->index < MEMORY_AREA_NUM, "kzmem_free failed");

    p = &pool[mp->index];
    ASSERT(mp->size == p->size, "kzmem_free failed");

    mp->next = p->free;
    p->free = mp;
}
